import { May } from "../type"
import Chunk from "./Chunk"

export interface UploadTaskOptions {
  chunkSize?: number //分片的限制
  chunkLimit?: number //最多分片的数量
  onerror?: (chunk: Chunk) => void
  onsuccess?: () => void
  onprocess?: (chunk: Chunk) => void
  onrun?: (chunk: Chunk) => void
  onabort?: (chunk: Chunk) => void
}

export type TaskStatus =
  | "init"
  | "ready"
  | "success"
  | "pending"
  | "error"
  | "abort"
  | "pause"
export type EventType =
  | "run"
  | "error"
  | "process"
  | "abort"
  | "success"
  | "pause"
class UploadTask {
  constructor(
    file?: File,
    remote?: string | ((chunk: Chunk) => Promise<any>),
    options: UploadTaskOptions = {},
  ) {
    this._file = file
    this._remote = remote
    this._chunkSize = options.chunkSize || 1 * 1024 * 1024
    this._onerror.push(options.onerror)
    this._onsuccess.push(options.onsuccess)
    this._onprocess.push(options.onprocess)
    this._onrun.push(options.onrun)
    this._onabort.push(options.onabort)
  }

  private _file: May<File>
  private _remote: May<string | ((chunk: Chunk) => Promise<any>)>
  private chunks: Chunk[] = []
  private _chunkSize: number
  private _status: TaskStatus = "init"
  private _error?: any
  private _curUploading: May<Chunk>

  private _onerror: any[] = []
  private _onsuccess: any[] = []
  private _onprocess: any[] = []
  private _onpause: any[] = []
  private _onrun: any[] = []
  private _onabort: any[] = []

  get onerror() {
    return this._onerror
  }
  set onerror(val: any) {
    this._onerror.push(val)
  }
  get onpause() {
    return this._onpause
  }
  set onpause(val: any) {
    this._onpause.push(val)
  }
  get onsuccess() {
    return this._onsuccess
  }
  set onsuccess(val: any) {
    this._onsuccess.push(val)
  }

  get onprocess() {
    return this._onprocess
  }
  set onprocess(val: any) {
    this._onprocess.push(val)
  }

  get onrun() {
    return this._onrun
  }
  set onrun(val: any) {
    this._onrun.push(val)
  }

  get onabort() {
    return this._onabort
  }
  set onabort(val: any) {
    this._onabort.push(val)
  }

  get file(): May<File> {
    return this._file
  }
  set file(val: May<File>) {
    this._file = val
  }
  get remote() {
    return this._remote
  }
  set remote(val: any) {
    this._remote = val
  }
  get chunkSize() {
    return this._chunkSize
  }
  set chunkSize(val) {
    this._chunkSize = val
  }
  get status() {
    return this._status
  }
  set status(val: TaskStatus) {
    this._status = val
  }
  get curUploading(): May<Chunk> {
    return this._curUploading
  }
  set curUploading(val: May<Chunk>) {
    this._curUploading = val
  }
  get error() {
    return this._error
  }
  set error(val: TaskStatus) {
    this.error = val
  }

  /**
   * @description 初始化
   */
  init() {
    this.file && this.split(this.file, this.chunkSize)
    this.status = "ready"
  }

  split(file: File, size: number) {
    let offset = 0,
      current: Chunk | undefined
    while (offset < file.size) {
      const blob = file.slice(offset, offset + size)
      const chunk = new Chunk(blob, offset)
      chunk.prev = current
      if (current) {
        current.next = chunk
      }
      current = chunk
      this.chunks.push(current)
      offset += size
    }
  }

  private emitEvents(type: EventType, payload?: any) {
    this[`on${type}`].forEach((event: any) => event?.(payload))
  }
  /**
   * @description 添加事件监听
   * @param type
   * @param callback
   */
  public on(type: EventType, callback: any) {
    const targetEvent = this[`_on${type}`]
    if (targetEvent instanceof Array) {
      targetEvent.push(callback)
      if (type === "process") {
        callback?.(
          this.status === "success"
            ? this.file?.size
            : this.curUploading?.offset || 0,
        )
      }
    } else {
      throw "unexpected event: " + type
    }
  }
  /**
   * @description 移除监听
   * @param type
   * @param callback
   */
  public remove(type: EventType, callback: any) {
    const targetEvent = this[`_on${type}`]
    if (targetEvent instanceof Array) {
      for (const i in targetEvent) {
        if (targetEvent[i] === callback) {
          targetEvent.splice(Number(i), 1)
          break
        }
      }
    } else {
      throw "unexpected event: " + type
    }
  }
  /**
   * @description 开始执行上传程序
   */
  async run() {
    this.chunks?.length ? void 0 : this.init()
    this.status = "pending"
    this.curUploading = this.curUploading || this.chunks[0]
    this.emitEvents("run")
    while (this.curUploading && this.status === "pending") {
      try {
        //const targetChunk = this.chunks[0]
        if (this.curUploading.status == "ready") {
          const chunkRes = await this.curUploading.upload(
            async (chunk: Chunk) => {
              await new Promise((r, j) => {
                setInterval(() => {
                  r(0)
                }, 100)
              })

              if (typeof this.remote === "string") {
                //todo use correct remote
                //await uploader('www.baidu.com', { method: 'POST', signal: chunk.controller.signal })
                this.emitEvents("process", chunk.offset + this.chunkSize)
              } else {
                //自定义的上传函数
                //await this.remote?.(chunk)
              }
            },
          )
        }
      } catch (err: any) {
        this.curUploading = undefined
        //此处的err是一个数组，返回当前的chunk上传的错误信息及chunk自身
        this.status = "error"
        this.error = err?.[0]
        this.emitEvents("error", err)
        throw err
      }
      this.curUploading = this.curUploading?.next
    }
    if (!this.curUploading) {
      this.curUploading = undefined
      this.status = "success"
      this.emitEvents("success")
    }
  }

  /**
   * @description 终止上传
   */
  abort() {
    this.status = "abort"
    this.curUploading?.abort()
    this.chunks = []
    this.curUploading = null
    this.file = undefined
    this.emitEvents("abort")

    // this._onerror = []
    // this._onsuccess = []
    // this._onabort = []
    // this._onpause = []
    // this._onrun = []
    // this._onprocess = []
  }

  pause() {
    this.curUploading?.abort()
    this.emitEvents("pause")
    this.status = "pause"
  }
}

//默认使用的上传方法
async function uploader(url: RequestInfo | URL, init: RequestInit) {
  const res = await fetch(url, init)
}

export default UploadTask
